package cn.edu.pku.sei.ra.util;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

import cn.edu.pku.sei.ra.framework.RARuntimeException;


public class ECoreRelatedUtil {
	
	public static ECoreRelatedUtil eINSTANCE=new ECoreRelatedUtil();
	
	protected ResourceSet resourceSet=null;
	
	public ECoreRelatedUtil(){
		Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xmi",
				new XMIResourceFactoryImpl());
		resourceSet = new ResourceSetImpl();
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put
		(
				Resource.Factory.Registry.DEFAULT_EXTENSION, 
				new XMIResourceFactoryImpl()
		);
		resourceSet.getPackageRegistry().put
		(EcorePackage.eNS_URI, 
				EcorePackage.eINSTANCE);
	}
	
	public EPackage getEPackage(URI uri){
		Resource rs=resourceSet.getResource(uri,true);
		for(Iterator it=rs.getContents().iterator();it.hasNext();)
		{
			Object ob=it.next();
			if(ob instanceof EPackage){
				return (EPackage)ob;
			}
		}
		return null;		
	}
	
	public void registerPackage(String modelURI,EPackage pack){
		resourceSet.getPackageRegistry().put(
				modelURI, 
				pack);
	}
	
	public ResourceSet getResourceSet(){
		return resourceSet;
	}
	
	public static void refreshEObject(EObject m){
		EClass mm=m.eClass();
		for(Iterator ita=mm.getEAllAttributes().iterator();ita.hasNext();){
			EAttribute attr=(EAttribute)ita.next();
			//m.eSet(attr, m.eGet(attr));
			m.eGet(attr);
		}
		for(Iterator itc=mm.getEAllContainments().iterator();itc.hasNext();){
			EReference ref=(EReference)itc.next();
			if(ref.getUpperBound()!=1){
				EList newml=(EList)m.eGet(ref);
				for(Object newmo:newml){
					refreshEObject((EObject) newmo);
				}
			}
		}
	}
	
	public static List<EStructuralFeature> getAllKeyFeatures(EClass eClass)
	{
		List<EStructuralFeature> list = new LinkedList<EStructuralFeature>();
		for (EStructuralFeature feature : eClass.getEAllStructuralFeatures())
			if (isPrimaryAttribute(feature))
				list.add(feature);
		return list;
	}
	
	public static boolean isPrimaryAttribute(EStructuralFeature attribute)
	{
		EAnnotation annotation = attribute.getEAnnotation(ANNOTATION_SOURCE);
		if (annotation == null) return false;
		if ("true".equals(annotation.getDetails().get("KeyAttribute")))
			return true;
		else
			return false;
	}
	
	public static boolean checkTheSame(EObject o1, EObject o2){
		List<EStructuralFeature> key1=ECoreRelatedUtil.getAllKeyFeatures(o1.eClass());
		return checkTheSame(o1,o2,key1);
	}
	
	public static boolean checkTheSame(EObject o1, EObject o2, List<EStructuralFeature> keys)
	{
		try{
			boolean same=true;
			for(EStructuralFeature key:keys){
				Object v1=o1.eGet(key);
				Object v2=o2.eGet(key);
				if(!v1.equals(v2)){
					same=false;
					break;
				}
			}
			return same;
		}
		catch(NullPointerException e){
			throw new RARuntimeException("Key Attribute value cannot be null");
		}
	}
	
	public static boolean checkTheSame(EObject o1, List<EStructuralFeature> k1, EObject o2, List<EStructuralFeature> k2)
	{
		try{
			boolean same=true;
			for(int i=0;i<k1.size();i++){
				Object v1=o1.eGet(k1.get(i));
				Object v2=o2.eGet(k2.get(i));
				if(!v1.equals(v2)){
					same=false;
					break;
				}
			}
			return same;
		}
		catch(NullPointerException e){
			throw new RARuntimeException("Key Attribute value cannot be null");
		}
	}
	
	public static boolean checkTheSameFromDifferentPackage(EObject o1, EObject o2)
	{
		List<EStructuralFeature> key1=ECoreRelatedUtil.getAllKeyFeatures(o1.eClass());
		List<EStructuralFeature> key2=ECoreRelatedUtil.getAllKeyFeatures(o2.eClass());
		return checkTheSame(o1,key1,o2,key2);
	}
	
	public static boolean checkTheSameForDiffTypes(EObject o1, EObject o2){
		try{
			if(o1.eClass()!=o2.eClass())
				return false;
			return checkTheSame(o1,o2);
		}
		catch(Exception e){
			return false;
		}
	}
	
	private static final String ANNOTATION_SOURCE = "http:///jp.ac.u_tokyo.wrapper.AttributeAnnotations";
}
